/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package co.edu.sd.jxta.socket;

import co.edu.sd.jxta.Command;
import co.edu.sd.jxta.IPeer;
import co.edu.sd.jxta.IPeerUI;
import co.edu.sd.jxta.msn.MessageMSPFactory;
import co.edu.sd.jxta.service.ServiceType;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.jxta.endpoint.Message;
import net.jxta.peergroup.PeerGroup;
import net.jxta.protocol.PipeAdvertisement;
import net.jxta.socket.JxtaSocket;

/**
 *
 * @author SD
 */
public class P2PClientSoccket
        extends CommonSocket
        implements Runnable {

    private PipeAdvertisement adv;
    private JxtaSocket client;
    private Thread daemon;
    private PeerGroup oPeerGroup;
    private Command cmd;

    public P2PClientSoccket(PipeAdvertisement adv, PeerGroup oPeerGroup, IPeer peer, Command cmd, IPeerUI peerUI) {
        this.peerUI = peerUI;
        this.adv = adv;
        this.oPeerGroup = oPeerGroup;
        this.peer = peer;
        this.cmd = cmd;
        this.peerUI = peerUI;
        try {
            initClient(oPeerGroup);
        } catch (IOException ex) {
            System.out.println("Starting ");
            ex.printStackTrace();
        }
        start();
    }

    public void start() {
        if (daemon == null) {
            daemon = new Thread(this);
            daemon.start();
        }
    }

    public void run() {
        try {

            setStreams(client);

            this.peerUI.append("Starting " + adv.getName() + " FLOW");

            Message msg = null;
            //DH Security
            try {
                msg = readMessage();
            } catch (SecurityException se) {
                output.flush();
                output.close();
                input.close();
                client.close();
                this.peerUI.append("--session finished for Security--");
                return;
            }

            /**security Agreement here*/
            
            switch (ServiceType.getTypeByID(adv.getName())) {
                case FILE_SEARCH:

                    writeMessage(MessageMSPFactory.createFileSearchRequestMessage(oPeerGroup.getPeerName(), "" + cmd.getParameter("" + Command.ParametersName.FILE_NAME)));
                    msg = readMessage();
                    String sFound = MessageMSPFactory.getStringFromMessage(msg, null, "" + MessageMSPFactory.Attributes.TAG_OPERATION_RESULT);
                    this.peerUI.append("file search result " + sFound);

                    if (!sFound.equals("true")) {
                        //if file was found on remote server go to FILE_DOWNLOAD Flow
                        writeMessage(MessageMSPFactory.createOperationSuccessMessage(oPeerGroup.getPeerName(), "true"));
                        break;
                    }
                case FILE_DOWNLOAD:
                    this.peerUI.append("requested File " + cmd.getParameter("" + Command.ParametersName.FILE_NAME));
                    writeMessage(MessageMSPFactory.createFileDownloadMessage(oPeerGroup.getPeerName(), "" + cmd.getParameter("" + Command.ParametersName.FILE_NAME)));

                    Message sessionmsg = readMessage();

                    this.peerUI.append("...." + MessageMSPFactory.getStringFromMessage(sessionmsg, "" + MessageMSPFactory.Attributes.TAG_PROCESS_ID));
                    this.peerUI.append("requested File " + MessageMSPFactory.getIntegerFromMessage(sessionmsg, "" + MessageMSPFactory.Attributes.TAG_SESSION_PIECES));
                    int pieces = MessageMSPFactory.getIntegerFromMessage(sessionmsg, "" + MessageMSPFactory.Attributes.TAG_SESSION_PIECES);
                    int j = 0;
                    List<Message> msgs = new ArrayList<Message>();
                    while (j < pieces) {
                        msg = readMessage();
                        msgs.add(msg);
                        if (hasToSaveState(j, pieces)) {
                            writeMessage(MessageMSPFactory.createGlobalStateMarkerMessage(oPeerGroup.getPeerName()));
                            Message ACK = readMessage();
                            peerUI.append("::saving state:: \t" + j);
                            peer.writeFile(msgs);
                            msgs = new ArrayList<Message>();
                        }
                        peerUI.append(oPeerGroup.getPeerName() + "::recieved piece: " + MessageMSPFactory.getIntegerFromMessage(msg, "" + MessageMSPFactory.Attributes.TAG_PIECE_ID));
                        j++;
                    }

                    break;
                case RECOVER_FILE_DOWNLOAD:
                    this.peerUI.append("Requested File " + cmd.getParameter("" + Command.ParametersName.FILE_NAME));
                    writeMessage(MessageMSPFactory.createFileDownloadMessage(oPeerGroup.getPeerName(), "" + cmd.getParameter("" + Command.ParametersName.FILE_NAME)));

                    Message sessionmsgRecoverFileDownload = readMessage();

                    this.peerUI.append("...." + MessageMSPFactory.getStringFromMessage(sessionmsgRecoverFileDownload, "" + MessageMSPFactory.Attributes.TAG_PROCESS_ID));
                    int piecesRecover = MessageMSPFactory.getIntegerFromMessage(sessionmsgRecoverFileDownload, "" + MessageMSPFactory.Attributes.TAG_SESSION_PIECES);
                    int currentSegment = MessageMSPFactory.getIntegerFromMessage(sessionmsgRecoverFileDownload, "" + MessageMSPFactory.Attributes.TAG_CURRENT_PIECE);

                    this.peerUI.append("Requested File pieces: " + piecesRecover);
                    this.peerUI.append("Requested File current segment: " + currentSegment);

                    List<Message> msgsRecoverFileDownload = new ArrayList<Message>();

                    while (currentSegment < piecesRecover) {
                        msg = readMessage();
                        msgsRecoverFileDownload.add(msg);
                        if (hasToSaveState(currentSegment, piecesRecover)) {
                            writeMessage(MessageMSPFactory.createGlobalStateMarkerMessage(oPeerGroup.getPeerName()));
                            Message ACK = readMessage();
                            peerUI.append("::saving state:: \t" + currentSegment);
                            peer.writeFile(msgsRecoverFileDownload);
                            msgsRecoverFileDownload = new ArrayList<Message>();
                        }
                        peerUI.append(oPeerGroup.getPeerName() + "recieved piece: " + MessageMSPFactory.getIntegerFromMessage(msg, "" + MessageMSPFactory.Attributes.TAG_PIECE_ID));
                        currentSegment++;
                    }

                    break;
                case FILE_UPLOAD:

                    List messagesUpload = MessageMSPFactory.createFileUploadMessagePieces(
                            oPeerGroup.getPeerName(), "" + cmd.getParameter("" + Command.ParametersName.FILE_NAME));

                    writeMessage(MessageMSPFactory.createFileUploadSessionMessage(
                            oPeerGroup.getPeerName(),
                            "" + cmd.getParameter("" + Command.ParametersName.FILE_NAME),
                            messagesUpload.size(),
                            0));

                    for (int i = 0; i < messagesUpload.size(); i++) {
                        Message current = (Message) messagesUpload.get(i);
                        this.peerUI.append(oPeerGroup.getPeerName() + "::sending piece " + i);
                        writeMessage(current);
                        try {
                            Thread.currentThread().sleep(50L);
                        } catch (InterruptedException ex) {
                        }

                    }
                    break;
                case RECOVER_FILE_UPLOAD:

                    //Read las segment of state
                    String fileName = String.valueOf(cmd.getParameter(Command.ParametersName.FILE_NAME.name()));

                    List messagesRecoverUpload = MessageMSPFactory.createFileUploadMessagePieces(
                            oPeerGroup.getPeerName(), fileName);

                    Message recoverMessage = MessageMSPFactory.createFileRecoverUploadSessionMessage(
                            oPeerGroup.getPeerName(),
                            "" + cmd.getParameter("" + Command.ParametersName.FILE_NAME),
                            messagesRecoverUpload.size());

                    if (recoverMessage == null) {
                        this.peerUI.append("No STATE for upload file: " + fileName);
                        return;
                    }

                    writeMessage(recoverMessage);

                    int currentPiece = MessageMSPFactory.getIntegerFromMessage(recoverMessage, MessageMSPFactory.Attributes.TAG_CURRENT_PIECE.name());

                    for (int i = currentPiece; i < messagesRecoverUpload.size(); i++) {
                        Message current = (Message) messagesRecoverUpload.get(i);
                        this.peerUI.append(oPeerGroup.getPeerName() + "::sending part " + i);
                        writeMessage(current);
                        try {
                            Thread.currentThread().sleep(50L);
                        } catch (InterruptedException ex) {
                            Logger.getLogger(P2PClientSoccket.class.getName()).log(Level.SEVERE, null, ex);
                        }
                    }

                    break;
                case APP_EXEC:
                    writeMessage(MessageMSPFactory.createExecuteApplicationMessageRequest(oPeerGroup.getPeerName(), "" + cmd.getParameter("" + Command.ParametersName.APP)));
                    msg = readMessage();
                    this.peerUI.append("------------------output------------------");
                    this.peerUI.append("" + MessageMSPFactory.getStringFromMessage(msg, null, "" + MessageMSPFactory.Attributes.TAG_APP_OUTPUT));
                    this.peerUI.append("------------------output------------------");
                    break;
                case UTC_TIME:
                    writeMessage(MessageMSPFactory.createTimeMessageRequest(oPeerGroup.getPeerName()));
                    msg = readMessage();
                    /**digest here*/
                    this.peerUI.append("The time is: " + MessageMSPFactory.getStringFromMessage(msg, null, "" + MessageMSPFactory.Attributes.TAG_TIME_OUTPUT));
                    break;
            }

            output.flush();
            output.close();
            input.close();
            client.close();
            this.peerUI.append("--session finished--");
        } catch (ClassNotFoundException ex) {
            Logger.getLogger(P2PClientSoccket.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            ex.printStackTrace();
        }

    }

    public void initClient(PeerGroup oPeerGroup) throws IOException {
        client = new JxtaSocket(oPeerGroup, null, adv, 5000, true);
    }
}
